﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace Inference
{
    public partial class ucSlotPicker : UserControl
    {
        TaleEntities tale;

        /// <summary>
        /// id домена-папы или объемлющего домена, -1 иначе. Нужно для выбора пронаследованного домена
        /// </summary>
        public int ParentDomainID
        {
            get;
            set;
        }

        /// <summary>
        /// тип домена-папы, -1 - если нет такого, 0 - если обычный, 1 - если фреймовый
        /// </summary>
        public int ParentDomainType
        {
            get;
            set;
        }

        /// <summary>
        /// режим СлотПикера, тру - если выбираем слот, фолс - если выбираем домен
        /// </summary>
        public bool SlotPickerMode
        {
            get;
            set;
        }

        /// <summary>
        /// номер слота, в который добавляется продукция. Необходимо, чтобы подгружать только предыдущие слоты
        /// </summary>
        public int TargetSlotNumber
        {
            get;
            set;
        }
        /// <summary>
        /// Фрейм, в рамках которого производится выбор
        /// </summary>
        public Frame TargetFrame
        {
            get;
            set;
        }
        /// <summary>
        /// строка пути до вбранного слота с имененм фрейма
        /// </summary>
        public string PathString
        {
            get
            {
                try
                {
                    return txbChain.Text;
                }
                catch
                {
                    return "";
                }
            }
        }
        /// <summary>
        /// строка пути до выбранного слота без имени фрейма
        /// </summary>
        public string PathStringCut
        {
            get
            {
                try
                {
                    return txbChain.Text.Substring(txbChain.Text.IndexOf("'.") + 2);
                }
                catch
                {
                    return "";
                }

            }
        }
        /// <summary>
        /// рид онли режим 
        /// </summary>
        public bool ReadOnly
        {
            get
            {
                return !tvChainEditor.Enabled;
            }
            set
            {
                tvChainEditor.Enabled = !value;
            }
        }
        /// <summary>
        /// показывать ли текстбокс в выбранным слотом
        /// </summary>
        public bool ShowPathString
        {
            get
            {
                return !spcMain.Panel2Collapsed;
            }
            set
            {
                spcMain.Panel2Collapsed = !value;
            }
        }
        /// <summary>
        /// true, если выбранный в дереве узел является листом
        /// </summary>
        public bool LeafIsSelected
        {
            get
            {
                return (tvChainEditor.SelectedNode != null) && (tvChainEditor.SelectedNode.Nodes.Count == 0);
            }
        }

        private void FindSlot(TreeNode root, string tag)
        {
            if (root.Tag!=null && root.Tag.ToString() == tag)
                temp = root;
            else
                for (int i = 0; i < root.Nodes.Count; i++)
                {
                    if (root.Nodes[i].Tag!=null && root.Nodes[i].Tag.ToString() == tag)
                        temp = root.Nodes[i];
                    else /*return*/ FindNode(root.Nodes[i], tag);
                }
        }

        public int Place
        {
            get
            {
                TreeNode tn = null;
                int i = 0;
                temp = null;
                if (tvChainEditor.SelectedNode != null)
                {
                    while (i < tvChainEditor.Nodes[0].Nodes.Count && temp == null)
                    {
                        FindSlot(tvChainEditor.Nodes[0].Nodes[i], tvChainEditor.SelectedNode.Tag.ToString());
                        if (temp != null)
                            tn = tvChainEditor.Nodes[0].Nodes[i];
                        i++;
                    }
                    if (tn != null)
                        return int.Parse(tn.Tag.ToString());
                    else
                        return -1;
                }
                else return -1;
            }
        }
        /// <summary>
        /// ID слота, выбранного в дереве, -1 если слота с таким айди не нашли
        /// </summary>
        public int SelectedSlotID
        {
            get
            {
                if (tvChainEditor.SelectedNode != null)
                    if (tvChainEditor.SelectedNode.Tag != null)
                    {
                        int id = 0;
                        if (int.TryParse(tvChainEditor.SelectedNode.Tag.ToString(), out id))
                        {
                            return id;
                        }
                    }
                return -1;
            }
            set
            {
                //SelectedSlotID = value;
                temp = null;
                int i = 0;
                string tag = value.ToString();
                while (i < tvChainEditor.Nodes.Count && temp == null)
                {
                    FindNode(tvChainEditor.Nodes[i], tag);
                    i++;
                }
                if (temp != null)
                    tvChainEditor.SelectedNode = temp;

            }
        }

        /// <summary>
        /// Слот, выбранный в дереве, null если слота с таким айди не нашли
        /// </summary>
        public Slot SelectedSlot
        {
            get
            {
                if (tvChainEditor.SelectedNode != null)
                    if (tvChainEditor.Tag != null)
                    {
                        int id = 0;
                        if (int.TryParse(tvChainEditor.Tag.ToString(), out id))
                        {
                            var slot = tale.Slot.Where(s => s.ID == id);

                            if (slot.Count() > 0)
                            {
                                return slot.First();
                            }
                        }
                    }
                return null;
            }
        }


        /// <summary>
        /// ID домена, выбранного в дереве, -1 если домена с таким айди не нашли
        /// </summary>
        public int SelectedDomainID
        {
            get
            {
                if (tvChainEditor.SelectedNode != null)
                    if (tvChainEditor.SelectedNode.Tag != null)
                    {
                        int id = 0;
                        if (int.TryParse(tvChainEditor.SelectedNode.Tag.ToString().Substring(1), out id))
                        {
                            return id;
                        }
                    }
                return -1;
            }
        }



        /// <summary>
        /// Домен, выбранный в дереве, null если домена с таким айди не нашли
        /// </summary>
        public Domains SelectedDomain
        {
            get
            {
                if (tvChainEditor.SelectedNode != null)
                    if (tvChainEditor.SelectedNode.Tag != null)
                    {
                        int id = 0;
                        if (int.TryParse(tvChainEditor.SelectedNode.Tag.ToString().Substring(1), out id))
                        {
                            var dom = tale.Domains.Where(d => d.ID == id);

                            if (dom.Count() > 0)
                            {
                                return dom.First();
                            }
                        }
                    }
                return null;
            }

            // set
            //{
            //    SelectedDomain = value;                
            //}
        }

        /// <summary>
        /// тип выбранного домена, -1 - если все плохо, 0 - если обычный, 1 - если фреймовый
        /// </summary>
        public int SelectedDomainType
        {
            get
            {
                if (tvChainEditor.SelectedNode != null)
                    if (tvChainEditor.SelectedNode.Tag != null)
                    {
                        string s = tvChainEditor.SelectedNode.Tag.ToString();
                        if (s[0] == 'f')
                        {
                            return 1;
                        }
                        else
                            if (s[0] == 'd')
                                return 0;
                    }
                return -1;
            }
        }

        private void FindNode(TreeNode root, string tag)
        {
            if (root.Tag !=null && root.Tag.ToString() == tag)
                //return root;
                temp = root;
            else
                for (int i = 0; i < root.Nodes.Count; i++)
                {
                    if (root.Nodes[i].Tag!=null && root.Nodes[i].Tag.ToString() == tag)
                        //return root.Nodes[i];
                        temp = root.Nodes[i];
                    else /*return*/ FindNode(root.Nodes[i], tag);
                }
            // return null;
        }


        private TreeNode temp = null;
        public void InitFromSlot(Slot slot)
        {
            string dom_tag = "";
            if (slot.DomainType != null && slot.DomainType != 0)
            {
                dom_tag = "f" + slot.DomainType.ToString();
            }
            else
            {
                dom_tag = "d" + slot.Domain.ToString();
            }

            int i = 0;
            while (i < tvChainEditor.Nodes.Count && temp == null)
            {
                FindNode(tvChainEditor.Nodes[i], dom_tag);
                i++;
            }
            if (temp != null)
                tvChainEditor.SelectedNode = temp;
        }


        public ucSlotPicker()
        {
            InitializeComponent();
        }

        /// <summary>
        /// инициализация слотПикера, как контрола для выбора слотов.
        /// </summary>
        /// <param name="targetFrame">фрейм, в котором выбираем</param>
        /// <param name="targetSlotNumber">номер (number) слота, в который добавляется продукция. Необходимо, чтобы подгружать только предыдущие слоты, -1  - если подгружать все </param>
        public bool IfConc = false;
        public void InitSlotPicker(Frame targetFrame, int targetSlotNumber)
        {
            SlotPickerMode = true;
            this.TargetFrame = targetFrame;
            this.Text = targetFrame.Name;            
            tale = new TaleEntities();
            if (!IfConc)
                TargetSlotNumber = targetSlotNumber;
            else
                TargetSlotNumber = targetSlotNumber + 1;
            tvChainEditor.Nodes.Clear();
            tvChainEditor.Nodes.Add(CreateSubFrameNode(targetFrame, TargetSlotNumber, tale));
            tvChainEditor.SelectedNode = tvChainEditor.Nodes[0];
        }

        /// <summary>
        /// инициализация контрола для выбора Любого домена
        /// </summary>
        public bool IfSubframe=false;
        public void InitDomainPicker()
        {
            SlotPickerMode = false;
            tale = new TaleEntities();
            tvChainEditor.Nodes.Clear();

            ParentDomainID = -1;
            ParentDomainType = -1;

            var roots = tale.Frame.Where(f => f.Is_a == null);

            foreach (var f in roots)
            {
                if (!(IfSubframe && f.Name.Trim().ToLower()=="ситуация"))
                    tvChainEditor.Nodes.Add(CreateISAFrameNode(f, tale));
            }
            tvChainEditor.SelectedNode = tvChainEditor.Nodes[0];

			//var simpleDomainRoots = tale.Domains.Where(d => d.ID != 1 && d.ID != 7);

			//foreach (var d in simpleDomainRoots)
			//{
			//    TreeNode tmp = new TreeNode(d.Name);
			//    tmp.Tag = "d" + d.ID.ToString();
			//    tvChainEditor.Nodes.Add(tmp);
			//}
			RebuildDomainHierarchy();
        }

		#region построение иерархии простых доменов
		public TreeNode FindIntvChainEditor(string text)
		{
			foreach (TreeNode tn in tvChainEditor.Nodes)
			{
				TreeNode res = FindTreeNode(tn, text);
				if (res != null)
					return res;
			}
			return null;
		}

		public TreeNode FindTreeNode(TreeNode root, string text)
		{
			if (root.Text == text)
			{
				return root;
			}
			foreach (TreeNode item in root.Nodes)
			{
				TreeNode res = FindTreeNode(item, text);
				if (res != null)
					return res;
			}
			return null;
		}
		public List<TreeNode> FindNotLeavesIntvChainEditor()
		{
			List<TreeNode> res = new List<TreeNode>();
			foreach (TreeNode tn in tvChainEditor.Nodes)
			{
				res.AddRange(FindNotLeaves(tn));
			}
			return res;
		}
		public List<TreeNode> FindNotLeaves(TreeNode root)
		{
			List<TreeNode> res = new List<TreeNode>();
			foreach (TreeNode item in root.Nodes)
			{
				if (item.Nodes.Count != 0)
				{
					res.Add(item);
					res.AddRange(FindNotLeaves(item));
				}
			}
			return res;
		}
		public void PostProcessDomainTreeNode(TreeNode tn)
		{
			var subDoms = GetExpandableNodes(tn);
			var privVals = GetUnexpandableNodes(tn);
			if (subDoms.Count > 0 && privVals.Count > 0)
			{
				foreach (TreeNode pv in privVals)
				{
					foreach (TreeNode ch in subDoms)
					{
						List<string> q = GetChildNodesTexts(ch);
						if (q.Contains(pv.Text))
						{
							if (pv.Parent != null)
								pv.Parent.Nodes.Remove(pv);
						}
					}
				}
			}
		}

		public string getText(TreeNode cur)
		{
			string s = "";
			s = cur.Text;
			int i = s.IndexOf('(') - 1;
			if (i > 0)
			{
				s = s.Substring(0, i);
			}
			return s;
		}

		public List<string> GetChildNodesTexts(TreeNode cur)
		{
			List<string> res = new List<string>();

			foreach (TreeNode tn in cur.Nodes)
			{
				if (tn.Nodes.Count == 0)
					res.Add(tn.Text);
			}

			return res;
		}

		public List<TreeNode> GetExpandableNodes(TreeNode cur)
		{
			List<TreeNode> res = new List<TreeNode>();

			foreach (TreeNode tn in cur.Nodes)
			{
				if (tn.Nodes.Count > 0)
					res.Add(tn);
			}

			return res;
		}

		public List<TreeNode> GetUnexpandableNodes(TreeNode cur)
		{
			List<TreeNode> res = new List<TreeNode>();

			foreach (TreeNode tn in cur.Nodes)
			{
				if (tn.Nodes.Count == 0)
					res.Add(tn);
			}

			return res;
		}
		public void RebuildDomainHierarchy()
		{
			tale = new TaleEntities();
		
			var sortedDoms = (from d in tale.Domains
							  where d.ID != 1 && d.ID != 7
							  orderby d.DomainsValues.Count descending, d.ID
							  select d).ToList();

			foreach (var d in sortedDoms)
			{
				Domains par = DomainHierarchier.GetParent(d.Name, tale);
				if (par != null)
				{
					TreeNode pn = FindIntvChainEditor(par.Name);
					if (pn != null)
					{
						TreeNode tmp = new TreeNode(d.Name);
						tmp.Tag = "d" + d.ID.ToString();
						pn.Nodes.Add(tmp);
					}
				}
				else
				{
					TreeNode tmp = new TreeNode(d.Name);
					tmp.Tag = "d" + d.ID.ToString();
					tvChainEditor.Nodes.Add(tmp);
				}
			}

			foreach (TreeNode tn in FindNotLeavesIntvChainEditor())
			{
				PostProcessDomainTreeNode(tn);
			}
		}	
		#endregion

		/// <summary>
        /// Иницилизация контрола для выбора домена в рамках наследования от домена-папы
        /// </summary>
        /// <param name="parentDomainID">айди домена-папы (или фрейма, если домен фреймовый)</param>
        /// <param name="parentDomainType">1 - если домен-папа является фреймом, 0  - если домен-папа является обычным доменом</param>
        public void InitDomainPicker(int parentDomainID, int parentDomainType)
        {
            ParentDomainID = parentDomainID;
            ParentDomainType = parentDomainType;
            SlotPickerMode = false;
            tale = new TaleEntities();
            tvChainEditor.Nodes.Clear();

            if (parentDomainType == 1)
            {
                var root = tale.Frame.Where(f => f.ID == parentDomainID).First();

                tvChainEditor.Nodes.Add(CreateISAFrameNode(root, tale));
            }
            else
                if (parentDomainType == 0)
                {
                    var root = tale.Domains.Where(d => d.ID == parentDomainID).First();

					RebuildDomainHierarchy();
					TreeNode roottn = FindIntvChainEditor(root.Name);
					tvChainEditor.Nodes.Clear();
					tvChainEditor.Nodes.Add(roottn);

                    //tvChainEditor.Nodes.Add(CreateDomainNode(root, tale));
                }

            tvChainEditor.SelectedNode = tvChainEditor.Nodes[0];
        }

        public void Reset()
        {
            if (SlotPickerMode)
            {
                InitSlotPicker(TargetFrame, TargetSlotNumber);
            }
            else
            {
                if (ParentDomainID == -1)
                {
                    InitDomainPicker();
                }
                else
                {
                    InitDomainPicker(ParentDomainID, ParentDomainType);
                }
            }
        }

        public delegate void PickedEventHandler();

        /// <summary>
        /// Происходит, когда был выбран слот или домен
        /// </summary>
        public event PickedEventHandler Picked;

        public delegate void BadPickedEventHandler();

        /// <summary>
        /// Происходит, когда был выбран НЕ (слот или домен)
        /// </summary>
        public event PickedEventHandler BadPicked;


        public TreeNode CreateSubFrameNode(Frame f, int border, TaleEntities tale)
        {
            TreeNode tn = new TreeNode(f.Name);

            var slots = from s in f.Slot
                        orderby s.Number
                        where (border == -1) || (s.Number < border)
                        select s;
            foreach (var s in slots)
            {
                if (s.DomainType != null && s.DomainType != 0)
                {
                    var ff = tale.Frame.Where(fff => fff.ID == s.DefaultValue);
                    if (ff.Count() > 0)
                    {
                        TreeNode t = CreateSubFrameNode(ff.First(), -1, tale);
                        t.Tag = s.ID;
                        t.Text = s.Name;
                        tn.Nodes.Add(t);
                    }
                }
                else
                {
                    TreeNode t = new TreeNode(s.Name);
                    t.Tag = s.ID;
                    tn.Nodes.Add(t);
                }
            }
            return tn;
        }
        public TreeNode CreateISAFrameNode(Frame f, TaleEntities tale)
        {
            TreeNode tn = new TreeNode(f.Name);
            tn.Tag = "f" + f.ID.ToString();
            var ff = CheckIf.ChildrenOf(f.Name, false, tale);

            foreach (var fc in ff)
            {
                tn.Nodes.Add(CreateISAFrameNode(fc, tale));
            }

            return tn;
        }
        public TreeNode CreateDomainNode(Domains d, TaleEntities tale)
        {
            TreeNode tn = new TreeNode(d.Name);
            tn.Tag = "d" + d.ID.ToString();
            //var ff = CheckIf.ChildrenOf(d.Name, false, tale);

            //foreach (var fc in ff)
            //{
            //	tn.Nodes.Add(CreateISAFrameNode(fc, tale));
            //}

            return tn;
        }

        private void tvChainEditor_AfterSelect(object sender, TreeViewEventArgs e)
        {
            if (SlotPickerMode)//если режим выбора слота
            {
                if (tvChainEditor.SelectedNode != null)
                {
                    if (tvChainEditor.SelectedNode.Nodes.Count == 0)
                    {
                        txbChain.Text = "'" + tvChainEditor.SelectedNode.FullPath.Replace(".", "'.'") + "'";
                        if (Picked != null)
                            Picked();
                    }
                    else
                    {
                        txbChain.Text = "";
                        if (BadPicked != null)
                            BadPicked();
                    }
                }
            }
            else// если режим выбора домена
            {
                if (tvChainEditor.SelectedNode != null)
                {
                    txbChain.Text = "'" + tvChainEditor.SelectedNode.FullPath.Replace(".", "'.'") + "'";
                    if (Picked != null)
                        Picked();
                }
            }
        }



        #region всякая фигня
        public void InitFromPathString(string path)
        {
            tvChainEditor.CollapseAll();
            TreeNode cur = tvChainEditor.Nodes[0];
            string[] ss = path.Split('.');
            if (cur.Text == ss[0].Split('\'')[1])
            {
                for (int lvl = 1; lvl < ss.Length; lvl++)
                {
                    string curSlot = ss[lvl].Split('\'')[1];
                    foreach (TreeNode tn in cur.Nodes)
                    {
                        if (tn.Text == curSlot)
                        {
                            tn.Expand();
                            tvChainEditor.SelectedNode = tn;
                            cur = tn;
                            break;
                        }
                    }
                }
            }
        }
        public void InitFromPathStringCut(string path)
        {
            tvChainEditor.CollapseAll();
            TreeNode cur = tvChainEditor.Nodes[0];
            string[] ss = path.Split('.');
            for (int lvl = 0; lvl < ss.Length; lvl++)
            {
                string curSlot = ss[lvl].Split('\'')[1];
                foreach (TreeNode tn in cur.Nodes)
                {
                    if (tn.Text == curSlot)
                    {
                        tn.Expand();
                        tvChainEditor.SelectedNode = tn;
                        cur = tn;
                        break;
                    }
                }
            }
        }
        public List<Frame> GetFramesPath()
        {
            List<Frame> res = new List<Frame>();
            if (tvChainEditor.SelectedNode != null)
            {
                string[] ss = tvChainEditor.SelectedNode.FullPath.Split('.');

                foreach (string s in ss)
                {
                    var ff = tale.Frame.Where(f => f.Name == s);
                    if (ff.Count() > 0)
                    {
                        res.Add(ff.First());
                    }
                }
            }
            return res;
        }
        /// <summary>
        /// Путь в виде коллекции строк-имен слотов
        /// </summary>
        /// <returns></returns>
        public List<string> GetStringsPath()
        {
            List<string> res = new List<string>();
            string[] ss = tvChainEditor.SelectedNode.FullPath.Split('.');

            foreach (string s in ss)
            {
                res.Add(s);
            }

            return res;
        }
        public List<Slot> GetSlotsPath()
        {
            List<Slot> res = new List<Slot>();
            if (tvChainEditor.SelectedNode != null)
            {
                string[] ss = tvChainEditor.SelectedNode.FullPath.Split('.');

                foreach (string s in ss)
                {
                    var ff = tale.Slot.Where(f => f.Name == s);
                    if (ff.Count() > 0)
                    {
                        res.Add(ff.First());
                    }
                }
            }
            return res;
        }
        #endregion
    }
}
